/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						       mine.cpp

	$Header: /game/mine.cpp $

	$NoKeywords: $

 ************************************************************************/

// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "mine.h"

#include "adventure_ai.h"
#include "adv_object_model.h"
#include "adv_object_strings.h"
#include "adventure_frame.h"
#include "adventure_map.h"
#include "adventure_map_window.h"
#include "adventure_frame.h"
#include "adventure_sounds.h"
#include "ai_value.h"
#include "army.h"
#include "army_info_window.h"
#include "basic_dialog.h"
#include "button.h"
#include "button_cache.h"
#include "combat_context_adv_object.h"
#include "combat_context_ptr.h"
#include "combat_result.h"
#include "external_string.h"
#include "hero_keyword_replacer.h"
#include "material_names.h"
#include "object_registration.h"
#include "player.h"
#include "player_color.h"
#include "replace_keywords.h"
#include "run_combat.h"
#include "script_context_args.h"
#include "script_targeting.h"
#include "simple_dialog.h"
#include "streambuf_operators.h"

namespace
{
	t_object_registration<t_mine> k_mine_registration( k_adv_object_mine );
	t_object_registration<t_adv_vein> k_vein_registration( k_adv_object_vein );
}

static char const* k_mine_model_name[] =
{ 
	"mine.gold",
	"mine.sawmill",
	"mine.ore pit",
	"mine.crystal",
	"mine.sulfur",
	"mine.alchemists lab",
	"mine.gem", 
};

static char const* k_mine_model_name_right[] =
{ 
	"mine.gold R",
	"mine.sawmill R",
	"mine.ore pit R",
	"mine.crystal R",
	"mine.sulfur R",
	"mine.alchemists lab R",
	"mine.gem R" 
};

extern t_button_cache g_cancel_button;

int get_mine_production(int material) 
{
	assert( material >= 0 && material < k_material_count );

	static const int mine_production[k_material_count] =  { 1000, 2, 2, 1, 1, 1, 1 };

	return mine_production[material];
}

static char const* get_model_name( t_material material, t_two_way_facing facing )
{
	if (facing == k_two_way_right)
		return k_mine_model_name_right[material];
	return k_mine_model_name[material];
}

// --------------------------------------------------------------
// mine which produces materials
// --------------------------------------------------------------
t_mine::t_mine(  std::string const& source ) 
	  : t_ownable_garrisonable_adv_object( source )
{
}

t_mine::t_mine( t_material material, t_two_way_facing facing)
      : t_ownable_garrisonable_adv_object( ::get_model_name( material, facing ) )
{
}

int t_mine::get_production( t_material type ) const
{
	return get_mine_production(type);
}

int t_mine::get_production() const
{
	return get_production(get_material());
}


// --------------------------------------------------------------
// mine which produces materials
// --------------------------------------------------------------
void t_mine::set_owner( int player_number )
{
	if (player_number == get_owner_number())
		return;

	if ( get_map() != 0 )
	{
		if (get_owner_number() >= 0)
			get_owner()->remove( this );
		t_ownable_garrisonable_adv_object::set_owner( player_number );
		if (get_owner_number() >= 0)
			get_owner()->add( this );
	}
	else
	{
		t_ownable_garrisonable_adv_object::set_owner( player_number );
	}
}


// --------------------------------------------------------------
// mine which produces materials
// --------------------------------------------------------------
void t_mine::process_new_day()
{
	t_ownable_garrisonable_adv_object::process_new_day( );

	if (get_owner_number() < 0)
		return;

	get_owner()->gain( get_material(), get_production() );
}

// --------------------------------------------------------------
// mine which produces materials
// --------------------------------------------------------------
void t_mine::activate_trigger( t_army* army, t_adv_map_point const& point, 
		                       t_direction direction, t_adventure_frame* frame )
{
	// Don't allow boats to trigger us
	// (also need to update one line in t_adventure_path_finder::search_to to change this rule)
	if (army->is_boat()) {
		return;
	}

	t_hero_keyword_replacer replacer;
	replacer.add_keyword( "%object_name", get_name() );

	// Get names.
	t_hero_keyword_replacer name_replacer;
	name_replacer.add_creatures( *this, false );
	m_creature_name_text = name_replacer( "%the_creatures" );

	int army_owner = army->get_owner_number();

	bool is_human = (army_owner >= 0) && !army->get_owner()->is_computer();

	if ( is_human ) 
		get_adventure_sound( k_adventure_sound_flag_mine )->play( get_sound_volume() );

    /*
    ** If not the same owner and there's a garrison army, go into combat.
    */
	bool enemy_owned = !same_team( army->get_owner(), get_owner() );

	if (enemy_owned)
    {
		if (t_creature_array::is_alive())
		{
			if ( is_human ) 
			{
				// Since mines are not normally guarded & they may not be expecting
				// a battle here, give player a change to change their mind.
				replacer.add_creatures( *this, true );

				t_counted_ptr<t_basic_dialog> dialog = new t_basic_dialog( frame );

				std::string text = replacer( get_text( "initial_guarded" ) );

				dialog->set_text( text );
				dialog->add_creatures( *this, true );
				dialog->add_cancel_button();
				dialog->add_ok_button();
				dialog->set_title( get_name() );
				dialog->open( get_dialog_bottom(point), true, k_align_bottom );


				if (!dialog->run_modal())
					return;
			}
			execute_script(k_garrison_scriptable_begin_combat, army);

			t_combat_context_ptr context;

			context = new t_combat_context_adv_object( army, this, point, 0, this );
			run_combat( *context );
			return;
		}
		else
		{
			if (army->get_living_hero_count() != 0)
				execute_script(k_garrison_scriptable_lose_combat, army);
		}

    } 
	visit( army, frame, false );
}


void t_mine::on_combat_end( t_army* army, t_combat_result result, t_creature_array* losses,
							t_adventure_frame* frame )
{
	t_adventure_map_ptr map = get_map();

	if (defender_lost( result ))
		set_can_remove_garrison( true ); // Original garrison is gone, new garrison is normal

	if (attacker_lost( result )) 
	{
		execute_script(k_garrison_scriptable_win_combat, army);
    	return;
	}

	if (army->get_living_hero_count() != 0)
	{
		execute_script(k_garrison_scriptable_lose_combat, army);
		if ( map->is_game_over() )
			return;
	}

	visit( army, frame, true );
}

void t_mine::visit( t_army* army, t_adventure_frame* frame, bool battle )
{
	t_adventure_map_ptr map = get_map();

	bool living_hero = army->get_living_hero_count() != 0;
	if (living_hero)
	{
		execute_script(k_garrison_scriptable_entered, army);
		if ( map->is_game_over() )
			return;
	}

	bool enemy_owned = !same_team( army->get_owner(), get_owner() );
	int army_owner = army->get_owner_number();

	if ( enemy_owned )
	{
		if (!living_hero)
		{
			if (army->is_human())
			{
				ok_dialog( get_text( "no_heroes" ), true );
			}
			return;
		}

		if ( !empty() )
			place_gravestone();

		// Switch owner. Don't bother user with garrison window unless
		// they visit mine again
		set_owner( army_owner );

		if ((army_owner == -1) || army->get_owner()->is_computer())
		{
			t_script_context_hero context_attacker(get_map(), NULL, army, army ? army->get_owner() : NULL, this, get_owner());
			return;
		}

		std::string name = get_name();
		std::string production;

		production = format_string( "%i %s", get_production(),
									get_material_name( get_material() ).c_str() );

		std::string text;
		t_hero_keyword_replacer replacer;

		replacer.add_keyword( "%object_name", get_name() );
		replacer.add_keyword( "%material", production );
		replacer.add_keyword( "%the_creatures", m_creature_name_text );

		text = replacer( get_text( battle ? "defeat" : "initial" ) );

		t_counted_ptr< t_basic_dialog > dialog = new t_basic_dialog( frame );
		dialog->set_text( text );
		dialog->set_title( get_name() );
		dialog->add_ok_button();
		dialog->add_material( get_material(), get_production(), true, true );
		dialog->open( get_dialog_bottom( army->get_position() ), true, k_align_bottom );
		dialog->run_modal();
	}
	else
	{
		if (army->get_owner()->is_computer())
			return;

		// Owner returns : show garrison window.
		run_garrison_dialog( army );
   	}
}


// --------------------------------------------------------------
// Double click on a mine
// --------------------------------------------------------------
void t_mine::left_double_click( t_mouse_event const& event, 
					            t_adventure_frame* adventure_frame )
{
	// Show garrison window if owner
	if (m_map->get_player_number() != get_owner_number())
		return;

	run_garrison_dialog( NULL );
}

// ---------------------------------------------------------------
// Cursor for the mine
// ---------------------------------------------------------------
t_bitmap_cursor const& t_mine::get_cursor( t_adventure_map_window const& window,
		                                   t_army const* army ) const
{
	if (same_team( army->get_owner(), get_owner()))
		return window.get_activate_cursor();
    if (empty())
		return window.get_activate_cursor();
	return window.get_attack_cursor();
}

// --------------------------------------------------------------
// Right click on a mine for information
// --------------------------------------------------------------
void t_mine::right_click( t_mouse_event const& event,
				          t_adventure_frame* adventure_frame )
{
	// Show help text if ungarrisoned
	if ( t_creature_array::empty() )
	{
		// The default
		t_ownable_garrisonable_adv_object::right_click( event, adventure_frame );
	}
	else
	{
		run_army_info_window( this, adventure_frame );
	}
}


// --------------------------------------------------------------
// mine which produces materials
// --------------------------------------------------------------
void t_mine::destroy()
{
	if (get_owner_number() >= 0)
		get_owner()->remove( this );
	t_ownable_garrisonable_adv_object::destroy();
}

// --------------------------------------------------------------
// mine which produces materials
// --------------------------------------------------------------
void t_mine::place( t_adventure_map& map, t_adv_map_point const& point )
{
	t_ownable_garrisonable_adv_object::place( map, point );

	if (get_owner_number() >= 0)
		get_owner()->add( this );

    int max;
    if (get_material() == k_gold)
        max = 1000;
    else
        max = 500;
    map.add_to_total_income ( max );
}

// --------------------------------------------------------------
// mine which produces materials
// --------------------------------------------------------------
void t_mine::initialize( t_adventure_map& map )
{
	t_ownable_garrisonable_adv_object::initialize( map );

	if ( get_owner_number() >= 0 )
	{
		get_owner()->add( this );
	}
}

int t_mine::get_version() const
{
	return 5;
}

bool t_mine::read( std::streambuf& stream, t_qualified_adv_object_type const& type, int version )
{
	if (version < 5)
		return t_ownable_garrisonable_adv_object::read_version( stream, type, version );
	return t_ownable_garrisonable_adv_object::read( stream, type );
}

// ---------------------------------------------------------------
// ---------------------------------------------------------------
void t_mine::read_postplacement( t_adventure_map& map )
{
	t_ownable_garrisonable_adv_object::read_postplacement( map );
}

// --------------------------------------------------------------
// --------------------------------------------------------------
float t_mine::ai_value( t_adventure_ai const& ai, t_creature_array const& army, int move_cost ) const
{
	t_player* player = army.get_owner();

	if (same_team( get_owner(), player))
	{
		// Coming Soon!  Consideration of a creature swap.
		return 0.0f;
	}
	if (army.get_living_hero_count() == 0)
		return 0.0f;

	return  player->get_material_value(get_material()) * 
			get_mine_production(get_material()) * 
			ai.scale_for_aggression( army.get_owner(), get_owner() ) *
			k_ai_mine_value_multiplier;
}

// --------------------------------------------------------------
// --------------------------------------------------------------
float t_mine::ai_activation_value_drop( t_creature_array const& army ) const
{
	if (army.get_owner() == get_owner())
		return 0.0f;
	
	return ::value_lost_in_combat( &army, this, true );
}

//
//
// Vein Adventure Object handling
//
//

// -------------------------------------------------------
// Vein object constructor
// -------------------------------------------------------
t_adv_vein::t_adv_vein( std::string const& source )
			  : t_stationary_adventure_object( source )
{
	m_material = static_cast<t_material>( get_major_subtype() );

	switch ( m_material ) 
	{
		case k_gold: 
			m_cost[k_gold] = 15000;
			m_cost[k_wood] = 20;
			m_cost[k_ore]  = 20;
			break;
		case k_wood: 
		case k_ore:	
			m_cost[k_gold] = 2500;
			break;
		case k_crystal:
		case k_sulfur:
		case k_gems:
			m_cost[k_gold] = 4000;
			m_cost[k_wood] = 10;
			break;
		case k_mercury:
			m_cost[k_gold] = 5000;
			break;
	}
}

// -------------------------------------------------------
// activate trigger for the Vein adventure map object
// -------------------------------------------------------
void t_adv_vein::activate_trigger( t_army* army, t_adv_map_point const& point, 
		                               t_direction direction, t_adventure_frame* frame )
{
	t_player*						  player = army->get_owner();
	t_material_array				  player_funds;
	t_two_way_facing				  facing;
	t_mine*							  mine;
	t_adventure_map*				  map;
	t_adv_map_point					  obj_point;
	std::string						  cost_text;
	t_counted_ptr<t_basic_dialog>	  dialog;
	std::string						  text;

	if (player == NULL)
		return;

	bool is_human = !player->is_computer();
	facing = t_two_way_facing( get_minor_subtype() );

	if (is_human)
	{
		dialog = new t_basic_dialog;
		player_funds = player->get_funds();
		if (player_funds < m_cost) 
		{

			int i;
			for (i = 0; i < k_material_count; i++)
				if (player_funds[i] > m_cost[i])
					player_funds[i] = 0;
				else
					player_funds[i] = m_cost[i] - player_funds[i];
			// send in a materials array to replace_keywords for 
			// the difference by which the player missed the cost
			// and pop up a rebuff
			text = get_text("insufficient.materials");
			text = replace_keywords(text, player_funds);
			dialog->set_text(text);
			dialog->add_ok_button();
			dialog->add_materials( player_funds, false );
			dialog->open( get_dialog_bottom( point ), true, k_align_bottom );
			dialog->run_modal();
			return;
		} 

		text = get_text( "Initial" );
		// add the artifact's name to the displayed text
		text = replace_keywords(text, "%material_name", get_material_name(m_material) );

		dialog->set_text( text );
		dialog->add_materials( m_cost, false );
		dialog->add_cancel_button();
		dialog->add_ok_button();
		dialog->open( get_dialog_bottom( point ), true, k_align_bottom );
		if (!dialog->run_modal())
			return;
	}
	else
	{
		t_material_array const& funds = player->ai_get_discretionary_funds();
		if (funds < m_cost)
			return;
		player->ai_set_discretionary_funds( funds - m_cost );
	}

	// destroy the vein
	destroy();

	// create a mine of the appropriate spot in its place
	mine = new t_mine( m_material, facing );
	obj_point = get_position();
	player->spend( m_cost );
	frame->update();

	// place the mine where the vein was
	map = frame->get_map();
	mine->place(*map, obj_point);
	mine->set_owner( army->get_owner_number() );

	if (is_human)
	{
		text = get_text("paid");
		dialog = new t_basic_dialog;
		dialog->set_text(text);
		dialog->add_ok_button();
		dialog->open( get_dialog_bottom( point ), true, k_align_bottom );
		dialog->run_modal();
	}
}

float t_adv_vein::ai_value( t_adventure_ai const& ai, t_creature_array const& army, int move_cost ) const
{
	t_player* player = army.get_owner();
	
	if (player->ai_get_discretionary_funds() < m_cost)
		return 0.0f;

	t_material_array adjusted_cost = player->get_material_values() * m_cost;

	return  (player->get_material_value(m_material) * 
			get_mine_production(m_material) * 
			k_ai_mine_value_multiplier) -
			adjusted_cost.get_sum();
}

void t_adv_vein::place( t_adventure_map& map, t_adv_map_point const& point )
{
    t_stationary_adventure_object::place( map, point );

    int max;
    if (m_material == k_gold)
        max = 1000;
    else
        max = 500;
    map.add_to_total_income ( max );
}

